Java中使用PowerMockito mock static方法/new对象/mock对象的public或private方法的简单示例 您所在的位置:网站首页 mock 打桩 Java中使用PowerMockito mock static方法/new对象/mock对象的public或private方法的简单示例

Java中使用PowerMockito mock static方法/new对象/mock对象的public或private方法的简单示例

2024-06-29 03:54| 来源: 网络整理| 查看: 265

1 针对方法打桩 1.1 打桩类的public static方法

测试用例中如果需要对public静态方法的打桩,针对测试类增加注解@RunWith(PowerMockRunner.class)同时针对静态方法所在的类增加注解@PrepareForTest({StaticMethod.class}),接着在测试用例调用方法之前增加

PowerMockito.mockStatic(StaticMethod.class); PowerMockito.when(StaticMethod.getJavaVersion()).thenReturn("1.8.0_92"); 或者用写法 PowerMockito.doReturn("1.8.0_92").when(StaticMethod.class, "getJavaVersion");

另外对PowerMockito的调用最好在产品类ProductClass对象new之前进行,这样保障一切模拟数据就绪后再进行产品类的测试,保证测试用例一次性成功编写的几率。

import junit.framework.TestCase; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({StaticMethod.class}) public class ProductClassTest extends TestCase { public void test_getCustomNameByVersion() { PowerMockito.mockStatic(StaticMethod.class); PowerMockito.when(StaticMethod.getJavaVersion()).thenReturn("1.8.0_92"); ProductClass product = new ProductClass(); assertEquals("18VersionSeries", product.getCustomNameByVersion()); } } ProductClassTest.java public class ProductClass { public String getCustomNameByVersion() { String javaVersion = StaticMethod.getJavaVersion(); if(javaVersion.split("_")[0].contains("1.8")) { return "18VersionSeries"; } else { return "OTHER"; } } } ProductClass.java产品代码调用其他类的静态方法 public class StaticMethod { public static String getJavaVersion() { throw new RuntimeException(); } } StaticMethod.java 1.2 打桩类的private static方法

 针对StaticMethod类中的private static方法打桩的时候,外部调用StaticMethod类的public方法仍然保持实际代码的调用,因此在模拟private static方法之前,增加一行

PowerMockito.spy(StaticMethod.class);或者

PowerMockito.when(StaticMethod.getJavaVersion()).thenCallRealMethod();

以此保证除了具体的某个方法打桩,其他的方法保持照旧。另外,针对private方法,因为正常是无法直接访问的,因此需要使用

下面的形式进行调用。这里要注意一个事情,doReturn when  和 when thenReturn的差异,后者是虽然做了打桩但是实际的代码还是会走一遍。

PowerMockito.doReturn(true).when(StaticMethod.class, "isProduct"); import junit.framework.TestCase; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({StaticMethod.class}) public class ProductClassTest extends TestCase { public void test_getCustomNameByVersion_product() throws Exception { PowerMockito.mockStatic(StaticMethod.class); PowerMockito.spy(StaticMethod.class); PowerMockito.doReturn(true).when(StaticMethod.class, "isProduct"); ProductClass product = new ProductClass(); assertEquals("18VersionSeries", product.getCustomNameByVersion()); } } ProductClassTest.java测试用例 public class StaticMethod { public static String getJavaVersion() { if (isProduct()) { System.out.println("getJavaVersion()::product"); return System.getProperty("java.version"); } System.out.println("getJavaVersion()::test"); throw new RuntimeException(); } private static boolean isProduct() { if (System.getenv().containsKey("isProduct=false")) { System.out.println("isProduct()::false"); return false; } else { System.out.println("isProduct()::true"); return true; } } } StaticMethod.java public class ProductClass { public String getCustomNameByVersion() { String javaVersion = StaticMethod.getJavaVersion(); if(javaVersion.split("_")[0].contains("1.8")) { return "18VersionSeries"; } else { return "OTHER"; } } } ProductClass.java 1.3 打桩类的public方法实现部分中使用的new对象

如果要对 StudentHandler.java的new对象进行mock,那么需要把StudentHandler对象所在的类StudentMng放到  @PrepareForTest({StudentMng.class})

需要特别注意,在PrepareForTest中放的类在统计代码覆盖率的时候,覆盖率是0。如果要针对该类进行代码测试覆盖,那需要以其他的方式进行模拟测试。

1 package training; 2 3 import junit.framework.TestCase; 4 import org.junit.runner.RunWith; 5 import org.powermock.api.mockito.PowerMockito; 6 import org.powermock.core.classloader.annotations.PrepareForTest; 7 import org.powermock.modules.junit4.PowerMockRunner; 8 9 10 import java.util.ArrayList; 11 import java.util.List; 12 @RunWith(PowerMockRunner.class) 13 @PrepareForTest({StudentMng.class}) 14 public class StudentMngTest extends TestCase { 15 16 17 public void test_getSpecifiedStudents() throws Exception { 18 StudentHandler handler = PowerMockito.mock(StudentHandler.class); 19 PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents()); 20 PowerMockito.whenNew(StudentHandler.class).withNoArguments().thenReturn(handler); 21 StudentMng mng = new StudentMng(); 22 assertEquals(1, mng.getSpecifiedStudents(10).size()); 23 } 24 25 private List mockStudents() { 26 List students = new ArrayList(); 27 Student stu = new Student(); 28 stu.setScore(new Score(80, 11)); 29 students.add(stu); 30 return students; 31 } 32 } StudentMngTest.java 1 package training; 2 3 import java.util.List; 4 5 public class StudentHandler { 6 public StudentHandler() { 7 System.out.println("invoke StudentHandler()"); 8 throw new RuntimeException(); 9 } 10 public List getAllStudents() { 11 System.out.println("invoke getAllStudents"); 12 throw new RuntimeException(); 13 } 14 } StudentHandler.java 是一个不方便直接new的类 1 package training; 2 3 public class Student { 4 private String sno; 5 private String name; 6 private Score score = new Score(100, 1000); 7 8 public String getName() { 9 System.out.println("invoke getName"); 10 return name; 11 } 12 13 public void setName(String name) { 14 System.out.println("invoke setName"); 15 this.name = name; 16 } 17 18 public String getSno() { 19 System.out.println("invoke getSno"); 20 return sno; 21 } 22 23 public void setSno(String sno) { 24 System.out.println("invoke setSno"); 25 this.sno = sno; 26 } 27 28 public void setScore(Score score) { 29 System.out.println("invoke setScore"); 30 this.score = score; 31 } 32 33 public Score getScore() { 34 System.out.println("invoke getScore"); 35 return score; 36 } 37 38 public String getScoreRecord(int delta) { 39 System.out.println("invoke getScoreRecord"); 40 return String.format("sno:%s,name:%s,value:%d,level:%d", sno, name, score.getValue() + delta, score.getLevel()); 41 } 42 } Student.java 1 package training; 2 3 public class Score { 4 private int value; 5 private int level; 6 7 public Score(int value, int level) { 8 this.value = value; 9 this.level = level; 10 } 11 12 public int getLevel() { 13 System.out.println("invoke getLevel"); 14 return level; 15 } 16 17 public void setLevel(int level) { 18 System.out.println("invoke setLevel"); 19 this.level = level; 20 } 21 22 public int getValue() { 23 System.out.println("invoke getValue"); 24 return value; 25 } 26 27 public void setValue(int value) { 28 System.out.println("invoke setValue"); 29 this.value = value; 30 } 31 } Score.java  1.4打桩类的public方法

 2种方式,方式一:PowerMockito.mock方式,对应StudentTest.java中的test_mock_public_method_powermock()测试用例

     方式二:函数复写override方式,对应StudentTest.java中的test_mock_public_method_override()测试用例

两种方式比较,方式一代码看起来简洁。 方式二 测试用例运行时间效率很高。

1 package training; 2 3 import junit.framework.TestCase; 4 import org.powermock.api.mockito.PowerMockito; 5 6 public class StudentTest extends TestCase { 7 public void test_mock_public_method_powermock() 8 { 9 Student stu = PowerMockito.mock(Student.class); 10 PowerMockito.when(stu.getName()).thenReturn("test_name"); 11 PowerMockito.when(stu.getNewName()).thenCallRealMethod(); 12 assertEquals("new_test_name", stu.getNewName()); 13 } 14 15 public void test_mock_public_method_override() 16 { 17 Student stu = new Student() { 18 @Override 19 public String getName() { 20 return "test_name"; 21 } 22 }; 23 assertEquals("new_test_name", stu.getNewName()); 24 } 25 } StudentTest.java 1 package training; 2 3 public class Student { 4 private String name; 5 6 7 public String getName() { 8 System.out.println("invoke getName"); 9 throw new RuntimeException(); 10 // return name; 11 } 12 13 public String getNewName() { 14 System.out.println("invoke getNewName"); 15 return "new_" + getName(); 16 } 17 18 } Student.java 1.5 打桩类的private方法

Student.java类中getName方法为private的,因此需要使用PowerMockito.when(stu, "getName").thenReturn("test_name");  方式进行mock。

被模拟的Student类需要在测试用例test类的开头增加以下两行。

@RunWith(PowerMockRunner.class)@PrepareForTest({Student.class})  1 package training; 2 3 import junit.framework.TestCase; 4 import org.junit.runner.RunWith; 5 import org.powermock.api.mockito.PowerMockito; 6 import org.powermock.core.classloader.annotations.PrepareForTest; 7 import org.powermock.modules.junit4.PowerMockRunner; 8 9 @RunWith(PowerMockRunner.class) 10 @PrepareForTest({Student.class}) 11 public class StudentTest extends TestCase { 12 public void test_mock_public_method_powermock() 13 { 14 Student stu = PowerMockito.mock(Student.class); 15 try { 16 PowerMockito.when(stu, "getName").thenReturn("test_name"); 17 } catch (Exception e) { 18 e.printStackTrace(); 19 } 20 PowerMockito.when(stu.getNewName()).thenCallRealMethod(); 21 assertEquals("new_test_name", stu.getNewName()); 22 } 23 24 } StudentTest.java 1 package training; 2 3 public class Student { 4 private String sno; 5 private String name; 6 private Score score = new Score(100, 1000); 7 8 private String getName() { 9 System.out.println("invoke getName"); 10 throw new RuntimeException(); 11 // return name; 12 } 13 14 public String getNewName() { 15 System.out.println("invoke getNewName"); 16 return "new_" + getName(); 17 } 18 19 public void setName(String name) { 20 System.out.println("invoke setName"); 21 this.name = name; 22 } 23 24 public String getSno() { 25 System.out.println("invoke getSno"); 26 return sno; 27 } 28 29 public void setSno(String sno) { 30 System.out.println("invoke setSno"); 31 this.sno = sno; 32 } 33 34 public void setScore(Score score) { 35 System.out.println("invoke setScore"); 36 this.score = score; 37 } 38 39 public Score getScore() { 40 System.out.println("invoke getScore"); 41 return score; 42 } 43 44 public String getScoreRecord(int delta) { 45 System.out.println("invoke getScoreRecord"); 46 return String.format("sno:%s,name:%s,value:%d,level:%d", sno, name, score.getValue() + delta, score.getLevel()); 47 } 48 } Student.java 2 针对变量打桩 2.1 打桩类的private成员变量

方法一: Whitebox

来源StudentMngTest.java,关键模拟代码如下,Whitebox.setInternalState(mng, "handler", handler);打桩设置对象的私有成员变量。

PowerMockito.when(mng.getSpecifiedStudents(10)).thenCallRealMethod();控制调用实际的方法。

StudentMng mng = PowerMockito.mock(StudentMng.class); 控制StudentMng对象的初始化过程,保证在对象初始化的时候不去做StudentHandler成员变量的初始化。

public void test_getSpecifiedStudents() { StudentHandler handler = PowerMockito.mock(StudentHandler.class); PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents()); StudentMng mng = PowerMockito.mock(StudentMng.class); Whitebox.setInternalState(mng, "handler", handler); PowerMockito.when(mng.getSpecifiedStudents(10)).thenCallRealMethod(); assertEquals(1, mng.getSpecifiedStudents(10).size());} package training; import junit.framework.TestCase; import org.powermock.api.mockito.PowerMockito; import org.powermock.reflect.Whitebox; import java.util.ArrayList; import java.util.List; public class StudentMngTest extends TestCase { public void test_getSpecifiedStudents() { StudentHandler handler = PowerMockito.mock(StudentHandler.class); PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents()); StudentMng mng = PowerMockito.mock(StudentMng.class); Whitebox.setInternalState(mng, "handler", handler); PowerMockito.when(mng.getSpecifiedStudents(10)).thenCallRealMethod(); assertEquals(1, mng.getSpecifiedStudents(10).size()); } private List mockStudents() { List students = new ArrayList(); Student stu = new Student(); stu.setScore(new Score(80, 11)); students.add(stu); return students; } } StudentMngTest.java 打桩后 连初始化StudentMng对象的时候都模拟完成。 package training; import java.util.List; public class StudentHandler { public StudentHandler() { System.out.println("invoke StudentHandler()"); // throw new RuntimeException(); } public List getAllStudents() { System.out.println("invoke getAllStudents"); throw new RuntimeException(); } } StudentHandler.java package training; public class Student { private String sno; private String name; private Score score = new Score(100, 1000); public String getName() { System.out.println("invoke getName"); return name; } public void setName(String name) { System.out.println("invoke setName"); this.name = name; } public String getSno() { System.out.println("invoke getSno"); return sno; } public void setSno(String sno) { System.out.println("invoke setSno"); this.sno = sno; } public void setScore(Score score) { System.out.println("invoke setScore"); this.score = score; } public Score getScore() { System.out.println("invoke getScore"); return score; } public String getScoreRecord(int delta) { System.out.println("invoke getScoreRecord"); return String.format("sno:%s,name:%s,value:%d,level:%d", sno, name, score.getValue() + delta, score.getLevel()); } } Student.java package training; public class Score { private int value; private int level; public Score(int value, int level) { this.value = value; this.level = level; } public int getLevel() { System.out.println("invoke getLevel"); return level; } public void setLevel(int level) { System.out.println("invoke setLevel"); this.level = level; } public int getValue() { System.out.println("invoke getValue"); return value; } public void setValue(int value) { System.out.println("invoke setValue"); this.value = value; } } Score.java

方法二(更加通用):用以下方式修改属性的访问权限,另外 @PrepareForTest({StudentMng.class}) 和 PowerMockito.whenNew(StudentHandler.class).withNoArguments().thenReturn(handler);  同时来达到mock new对象的目的。

Field handlerField = mng.getClass().getDeclaredField("handler");handlerField.setAccessible(true);handlerField.set(mng, handler); 1 package training; 2 3 import junit.framework.TestCase; 4 import org.junit.runner.RunWith; 5 import org.powermock.api.mockito.PowerMockito; 6 import org.powermock.core.classloader.annotations.PrepareForTest; 7 import org.powermock.modules.junit4.PowerMockRunner; 8 9 import java.lang.reflect.Field; 10 import java.util.ArrayList; 11 import java.util.List; 12 13 @RunWith(PowerMockRunner.class) 14 @PrepareForTest({StudentMng.class}) 15 public class StudentMngTest extends TestCase { 16 public void test_getSpecifiedStudents() throws Exception { 17 StudentHandler handler = PowerMockito.mock(StudentHandler.class); 18 PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents()); 19 PowerMockito.whenNew(StudentHandler.class).withNoArguments().thenReturn(handler); 20 StudentMng mng = new StudentMng(); 21 Field handlerField = mng.getClass().getDeclaredField("handler"); 22 handlerField.setAccessible(true); 23 handlerField.set(mng, handler); 24 assertEquals(1, mng.getSpecifiedStudents(10).size()); 25 } 26 27 private List mockStudents() { 28 List students = new ArrayList(); 29 Student stu = new Student(); 30 stu.setScore(new Score(80, 11)); 31 students.add(stu); 32 return students; 33 } 34 } StudentMngTest.java 2.2 打桩类的public static变量或者private static变量

针对此类情况,建议对static变量赋值的部分进行mock模拟,以此来达到模拟变量值得目的。

3 测试用例执行效率简单说明

override方式模拟打桩方式的执行测试用例时间消耗



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有